#ifndef BACKEND_H
#define BACKEND_H

#include <QString>
#include <QImage>
#include <QTimer>
#include <QElapsedTimer>
#include <CL/cl.hpp>

#include <QThread>
#include <QFuture>
#include <QtCore>
#include <QtConcurrent/QtConcurrentRun>
#include <QFutureWatcher>

#include "filter.h"
#include "transforminfo.h"

// !!! IMPORTANT:
// From Qt doc: The buffer must remain valid throughout the life of the QImage.

// !!! VERY IMPORTANT !!!
// FLOAT PRECISION CAN BE DIFFERENT IN OPENCL AND HOST

/// Application logic
class Backend : public QObject
{
Q_OBJECT
    QString _originalImageLocation;
    QString _modifiedImageLocation;
    QImage* _originalImage;
    QImage* _modifiedImage;
    Filter* _filter;
    bool _isParallel;
    int* _modifiedData;
    int _time;

    cl::Platform _platform;
    cl::Device _device;
    bool _platformIsSet;
    bool _deviceIsSet;

    void setup_filter();
    void setup_result();
    void setup_result_buffer();
private slots:
    void progress(int);
signals:
    void finished(double time);
    void progress_to_gui(int);
public:
    Backend();
    ~Backend();
    void load_image(const QString location);
    void save(const QString location);
    void set_parallel(bool isParallel);

    QImage* blur(float *filterMatrix, const int matrixWidth, TransformInfo& info);
    QImage* motion_blur(float *filterMatrix, TransformInfo& info);
    QImage* find_horizontal_edges(TransformInfo& info);
    QImage* find_vertical_edges(TransformInfo& info);
    QImage* find_45_degree_edges(TransformInfo& info);
    QImage* find_all_edges(TransformInfo& info);
    QImage* sharpen(TransformInfo& info);
    QImage* excessive_edges(TransformInfo& info);
    QImage* emboss(TransformInfo& info);
    int progress_min() const { return 0; }
    int progress_max() const { return _originalImage->width() * _originalImage->height() - 1; }

    void avalaible_platforms(std::vector<cl::Platform>& platforms) const;
    //void avalaible_devices(cl::Platform platform, std::vector<cl::Device>& devices) const;
    void set_platform(cl::Platform platform);
    void set_device(cl::Device device);

    QImage* get_original_image() const { return _originalImage; }

    void swap();
};

#endif // BACKEND_H
